/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "Vector.h"
#include <iostream>

namespace math {

	Vector2i Vector2i::UNDEFINED = Vector2i(static_cast<int>(-INF), static_cast<int>(-INF));
	Vector2f Vector2f::UNDEFINED = Vector2f(-INF, -INF);

	Vector3 Vector3::UNDEFINED = Vector3(-INF, -INF, -INF);
	Vector4 Vector4::UNDEFINED = Vector4(-INF, -INF, -INF, 0.0);
	
	const Vector3& Vector3::operator=(const Vector4 &vec)
	{
		// TODO: oder doch * statt /
		x=vec.x / vec.w;
		y=vec.y / vec.w;
		z=vec.z / vec.w;
		return *this;
	}
	
	const Vector3 Vector3::crossProduct(const Vector3 &vec) const
	{
		return Vector3(y*vec.z - z*vec.y, z*vec.x - x*vec.z, x*vec.y - y*vec.x);
	}

	const vec_t Vector3::dotProduct(const Vector3 &vec) const
	{
		return x*vec.x + y*vec.y + z*vec.z;
	}

	const vec_t Vector3::length() const
	{
		return (vec_t)sqrt(x*x + y*y + z*z);
	}

	const vec_t Vector3::lengthSquared() const
	{
		return (x*x + y*y + z*z);
	}

	const Vector3 Vector3::unitVector() const
	{
		return (*this) / length();
	}

    // return angle between two vectors
	const vec_t Vector3::angle(const Vector3& normal) const
	{
		float x = (*this).dotProduct(normal);	  
		if (x>=1)
			x = 1.0f;
		if (x<=-1)
			x = -1.0f;
		return acos(x);
	}

	// reflect this vector off surface with normal vector
	const Vector3 Vector3::reflection(const Vector3& normal) const
	{
		const Vector3 vec(*this | 1);     // normalize this vector
		return (vec - normal * 2.0 * (vec % normal)) * !*this;
	}

	const vec_t Vector3::distance(const Vector3 &vec) const
	{
		return ((vec_t) sqrt( (vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y) + (vec.z-z)*(vec.z-z) ) );
	}

	const vec_t	Vector3::sqrDistance(const Vector3 &vec) const
	{
		return (vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y) + (vec.z-z)*(vec.z-z);
	}

	const Vector3 Vector3::lerp(const Vector3 &vec1, const Vector3 &vec2, const float l)
	{
		if (l <= 0)
			return vec1;
		else if	(l >= 1)
			return vec2;
		else
			return ((1-l)*vec1 + l*vec2);
	}

	void Vector3::normalize()
	{
		float t = length();
		if (t != 0) {
			vec_t l = 1.0f/t;
			(*this) *= l;
		}
		else setZero();
	}

	void Vector3::setZero()
	{
		x = y = z = 0.0;
	}

	void Vector3::set(vec_t _x, vec_t _y, vec_t _z)
	{
		x = _x; y = _y; z = _z;
	}

	void Vector3::setBicubicInterpolation(const Vector3 &A, const Vector3 &TA, const Vector3 &TB, const Vector3 &B, vec_t t)
	{
		vec_t a,b,c,d;
		vec_t t3 = t*t*t;
		vec_t t2 = t*t;
		a=2*t3 - 3*t2 + 1;
		b=-2*t3 + 3*t2;
		c=t3 - 2*t2 + t;
		d=t3 - t2;
		x=a*A.x + b*B.x + c*TA.x + d*TB.x;
		y=a*A.y + b*B.y + c*TA.y + d*TB.y;
		z=a*A.z + b*B.z + c*TA.z + d*TB.z;
	}

	void Vector3::setAsNormal(const Vector3 &A, const Vector3 &B, const Vector3 &C)
	{
		Vector3 P=C-B;
		Vector3 Q=A-B;
		Vector3 n=P;
		n = crossProduct(Q);
		x=n.x;
		y=n.y;
		z=n.z;
		normalize();
	}

	void Vector3::normCrossProduct(const Vector3 &v1, const Vector3 &v2, Vector3 &out)
	{
		out.x = v1.y*v2.z - v1.z*v2.y;
		out.y = v1.z*v2.x - v1.x*v2.z;
		out.z = v1.x*v2.y - v1.y*v2.x;
		out.normalize();
	}

	void Vector3::transformBy(const Vector3 &position, float *m)
	{
		x += position.x; 
		y += position.y; 
		z += position.z;
		Vector3 vec;
 		vec.x = x * m[0] + y * m[4] + z * m[8] + m[12];
		vec.y = x * m[1] + y * m[5] + z * m[9] + m[13];
		vec.z = x * m[2] + y * m[6] + z * m[10] + m[14];
		x = vec.x; y = vec.y; z = vec.z;
	}

	const Vector3 Vector3::getDistanceVectorFromLine(const Vector3 &pointOnLine, const Vector3 &direction)
	{
		float t = (*this - pointOnLine).dotProduct(direction)/direction.length();
		Vector3 vec = pointOnLine + t*direction;
		return (*this - vec);
	}

	void Vector3::print() const
	{
		std::cout << x << "\t/\t" << y << "\t/\t" << z << std::endl;
	}

	const Vector3 Vector3::floor(const Vector3 &v)
	{
		Vector3 res = *this;
		if( v.x < x ) res.x = v.x;
		if( v.y < y ) res.y = v.y;
		if( v.z < z ) res.z = v.z;
		return res;
	}

	const Vector3 Vector3::ceil(const Vector3 &v)
	{
		Vector3 res = *this;
		if( v.x > x ) res.x = v.x;
		if( v.y > y ) res.y = v.y;
		if( v.z > z ) res.z = v.z;
		return res;
	}

	Vector3 Vector3::operator*(const math::Matrix4 &mat) const
	{
		return Vector3( this->x * mat[0] + this->y * mat[4] + this->z * mat[8] + mat[12],
						this->x * mat[1] + this->y * mat[5] + this->z * mat[9] + mat[13],
						this->x * mat[2] + this->y * mat[6] + this->z * mat[10] + mat[14] );
	}


// and now the vec4 definitions:


	Vector4::Vector4(vec_t a, vec_t b, vec_t c, vec_t d)
			: x(a), y(b), z(c), w(d)
	{
	}

	Vector4::Vector4(const Vector3 &vec, vec_t d)
	{
		x = vec.x;
		y = vec.y;
		z = vec.z;
		w = d;
	}

	Vector4::Vector4(const Vector4 &vec)
	{
		x = vec.x;
		y = vec.y;
		z = vec.z;
		w = vec.w;
	}
	/*
		const Vector4 Vector4::crossProduct(const Vector4 &vec) const
		{
			return Vector4(y*vec.z - z*vec.y, z*vec.x - x*vec.z, x*vec.y - y*vec.x);
		}
	*/
	const vec_t Vector4::dotProduct(const Vector4 &vec) const
	{
		return x*vec.x + y*vec.y + z*vec.z + w*vec.w;
	}

	const vec_t Vector4::length() const
	{
		return (vec_t)sqrt(x*x + y*y + z*z + w*w);
	}

	const vec_t Vector4::lengthSquared() const
	{
		return (x*x + y*y + z*z + w*w);
	}

	const Vector4 Vector4::unitVector() const
	{
		return (*this) / length();
	}

	// return angle between two vectors
	const vec_t Vector4::angle(const Vector4& normal) const
	{
		return acos(*this % normal);
	}

	// reflect this vector off surface with normal vector
	const Vector4 Vector4::reflection(const Vector4& normal) const
	{
		const Vector4 vec(*this | 1);     // normalize this vector
		return (vec - normal * 2.0 * (vec % normal)) * !*this;
	}

	const vec_t Vector4::distance(const Vector4& vec)
	{
		return ((vec_t) sqrt( (vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y) + (vec.z-z)*(vec.z-z) + (vec.w-w)*(vec.w-w)) );
	}

	const Vector4 Vector4::lerp(const Vector4 &vec1, const Vector4 &vec2, const float l)
	{
		if (l <= 0)
			return vec1;
		else if (l >= 1)
			return vec2;
		else
			return (vec1 + l*(vec2 - vec1));
	}

	void Vector4::normalize()
	{
		vec_t l = 1.0f/length();
		(*this) *= l;
	}

	void Vector4::setZero()
	{
		x = y = z = w = 0.0;
	}

	void Vector4::print() const
	{
		std::cout << x << " \t/\t" << y << "\t/\t" << z << "\t/\t" << w << std::endl;
	}

	Vector4 Vector4::operator*(const math::Matrix4 &mat) const
	{
		return Vector4( this->x * mat[0] + this->y * mat[4] + this->z * mat[8] + this->w * mat[12],
						this->x * mat[1] + this->y * mat[5] + this->z * mat[9] + this->w * mat[13],
						this->x * mat[2] + this->y * mat[6] + this->z * mat[10] + this->w * mat[14],
						this->x * mat[3] + this->y * mat[7] + this->z * mat[11] + this->w * mat[15]	);
	}


}
